#pragma once
#include <complex>

using namespace System;

namespace LatoolNet {

	///
	///<summary> This class stands for a complex value of double type. </summary>
	///
	public value class Complex {

	private:

		double m_real;
		double m_imag;

		Complex(std::complex<double> c) {
			m_real = c.real();
			m_imag = c.imag();
		};

	public:

		Complex(double real, double imag) {
			m_real = real;
			m_imag = imag;
		};

		property double Real {
			double get() { return m_real;}
			void set(double value) {m_real = value;}
		};

		property double Imag {
			double get() { return m_imag;}
			void set(double value) {m_imag = value;}
		};

		property double Abs {
			double get() { return Math::Sqrt(m_real * m_real + m_imag * m_imag); }
		};

		static bool operator ==(const Complex lhs, const Complex rhs) {
			return (lhs.m_real == rhs.m_real) && (lhs.m_imag == rhs.m_imag);
		};

		static Complex operator *(const Complex lhs, const Complex rhs) {
			std::complex<double> l(lhs.m_real, lhs.m_imag);
			std::complex<double> r(rhs.m_real, rhs.m_imag);
			return Complex(l * r);
		};

		static Complex operator *(const double d, const Complex rhs) {
			return Complex(d, 0) * rhs;
		};

		static Complex operator *(const Complex lhs, const double d) {
			return lhs * Complex(d, 0);
		};

		static Complex operator /(const Complex lhs, const Complex rhs) {
			std::complex<double> l(lhs.m_real, lhs.m_imag);
			std::complex<double> r(rhs.m_real, rhs.m_imag);
			return Complex(l / r);
		};

		static Complex operator /(const double d, const Complex rhs) {
			return Complex(d, 0) / rhs;
		};

		static Complex operator /(const Complex lhs, const double d) {
			return lhs / Complex(d, 0);
		};

		static Complex operator +(const Complex lhs, const Complex rhs) {
			std::complex<double> l(lhs.m_real, lhs.m_imag);
			std::complex<double> r(rhs.m_real, rhs.m_imag);
			return Complex(l + r);
		};

		static Complex operator +(const double d, const Complex rhs) {
			return Complex(d, 0) + rhs;
		};

		static Complex operator +(const Complex lhs, const double d) {
			return lhs + Complex(d, 0);
		};

		static Complex operator -(const Complex lhs, const Complex rhs) {
			std::complex<double> l(lhs.m_real, lhs.m_imag);
			std::complex<double> r(rhs.m_real, rhs.m_imag);
			return Complex(l - r);
		};

		static Complex operator -(const double d, const Complex rhs) {
			return Complex(d, 0) - rhs;
		};

		static Complex operator -(const Complex lhs, const double d) {
			return lhs - Complex(d, 0);
		};

		static Complex Pow(Complex base, Complex power) {
			std::complex<double> cbase(base.m_real, base.m_imag);
			std::complex<double> cpower(power.m_real, power.m_imag);
			return Complex(std::pow(cbase, cpower));
		};

		static Complex Pow(Complex base, double power) {
			return Pow(base, Complex(power, 0));	
		};

		static Complex Pow(double base, Complex power) {
			return Pow(Complex(base, 0), power);	
		};

		static Complex Conj(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::conj(comp));
		};

		static double Norm(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return std::norm(comp);
		};

		static Complex Sqrt(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::sqrt(comp));
		};

		static Complex Exp(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::exp(comp));
		};

		static Complex Log(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::log(comp));
		};

		static Complex Log10(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::log10(comp));
		};

		static Complex Sin(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::sin(comp));
		};

		static Complex Sinh(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::sinh(comp));
		};

		static Complex Cos(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::cos(comp));
		};

		static Complex Cosh(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::cosh(comp));
		};

		static Complex Tan(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::tan(comp));
		};

		static Complex Tanh(Complex c) {
			std::complex<double> comp(c.m_real, c.m_imag);
			return Complex(std::tanh(comp));
		};

		virtual String ^ ToString() new {
			return ToString(4);
		};

		String ^ ToString(int numDigit) {
			return ToString("(", ",", ")", numDigit);
		};

		String ^ ToString(String^ beginStr, String^ separator, String^ endStr, int numDigit) {
			String^ strReal = String::Format("{0:f" + numDigit + "}", m_real);	
			String^ strImag = String::Format("{0:f" + numDigit + "}", m_imag);	

			return beginStr + strReal + separator + strImag + endStr;
		};

	};

}
